{
 "cells": [
  {
   "cell_type": "markdown",
   "source": [
    "在这里，我们将演示如何将一个LangChain Runnable转换成一个可以被代理、链或聊天模型使用的工具.\n",
    "将langchain转换为tool 存现限制条件:\n",
    "- 它们的输入被限制为可序列化，特别是字符串和Python dict对象；\n",
    "- 它们包含名称和说明，说明如何以及何时使用它们；\n",
    "- 它们可能包含详细的args_schema作为参数。也就是说，虽然工具(作为Runnable)可能接受单个dict输入，但是填充dict所需的特定键和类型信息应该在args_schema中指定。\n",
    "\n",
    "## 用法\n",
    "### 带有类型信息的字典输入"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "9fe7b09f3f951780"
  },
  {
   "cell_type": "code",
   "outputs": [],
   "source": [
    "from langchain_core.runnables import RunnableLambda\n",
    "from typing_extensions import TypedDict\n",
    "from typing import List\n",
    "\n",
    "\n",
    "class Args(TypedDict):\n",
    "    a: int\n",
    "    b: List[int]\n",
    "\n",
    "\n",
    "def f(x: Args) -> str:\n",
    "    return str(x[\"a\"] * max(x[\"b\"]))\n",
    "\n",
    "\n",
    "runnable = RunnableLambda(f)\n",
    "\n",
    "as_tool = runnable.as_tool(\n",
    "    name=\"my tool\",\n",
    "    description=\"Explanation of when to use tool.\"\n",
    ")"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-31T02:08:26.221216Z",
     "start_time": "2024-10-31T02:08:26.211578Z"
    }
   },
   "id": "97855d9a98351fd0",
   "execution_count": 45
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Explanation of when to use tool.\n"
     ]
    },
    {
     "data": {
      "text/plain": "{'properties': {'a': {'title': 'A', 'type': 'integer'},\n  'b': {'items': {'type': 'integer'}, 'title': 'B', 'type': 'array'}},\n 'required': ['a', 'b'],\n 'title': 'my tool',\n 'type': 'object'}"
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "print(as_tool.description)\n",
    "as_tool.args_schema.schema()"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-31T02:08:26.236382Z",
     "start_time": "2024-10-31T02:08:26.230395Z"
    }
   },
   "id": "35c1efca8fbb3d92",
   "execution_count": 46
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "'6'"
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "as_tool.invoke({\"a\": 2, \"b\": [1, 2, 3]})"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-31T02:08:26.246080Z",
     "start_time": "2024-10-31T02:08:26.239233Z"
    }
   },
   "id": "937e997b7b39b0a8",
   "execution_count": 47
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 通过arg_types来指定参数类型\n",
    "如果没有类型信息，可以通过arg_types来指定参数类型。"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "f61040a23bb1cc8b"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "{'properties': {'a': {'title': 'A', 'type': 'integer'},\n  'b': {'items': {'type': 'integer'}, 'title': 'B', 'type': 'array'}},\n 'required': ['a', 'b'],\n 'title': 'My tool',\n 'type': 'object'}"
     },
     "execution_count": 48,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from typing import Dict, Any\n",
    "\n",
    "\n",
    "def g(x: Dict[str, Any]) -> str:\n",
    "    return str(x[\"a\"] * max(x[\"b\"]))\n",
    "\n",
    "\n",
    "runnable = RunnableLambda(g)\n",
    "as_tool = runnable.as_tool(\n",
    "    name=\"My tool\",\n",
    "    description=\"Explanation of when to use tool.\",\n",
    "    arg_types={\"a\": int, \"b\": List[int]},\n",
    ")\n",
    "as_tool.args_schema.schema()"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-31T02:08:26.255763Z",
     "start_time": "2024-10-31T02:08:26.247307Z"
    }
   },
   "id": "6f0bb5c28e3527c4",
   "execution_count": 48
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 通过直接传递工具所需的args_schema来完全指定模式"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "50ed948e734e2caa"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "{'description': 'Apply a function to an integer and list of integers.',\n 'properties': {'a': {'description': 'Integer',\n   'title': 'A',\n   'type': 'integer'},\n  'b': {'description': 'List of ints',\n   'items': {'type': 'integer'},\n   'title': 'B',\n   'type': 'array'}},\n 'required': ['a', 'b'],\n 'title': 'GSchema',\n 'type': 'object'}"
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from pydantic import BaseModel, Field\n",
    "\n",
    "\n",
    "class GSchema(BaseModel):\n",
    "    \"\"\"Apply a function to an integer and list of integers.\"\"\"\n",
    "\n",
    "    a: int = Field(..., description=\"Integer\")\n",
    "    b: List[int] = Field(..., description=\"List of ints\")\n",
    "\n",
    "\n",
    "runnable = RunnableLambda(g)\n",
    "runnable.as_tool(GSchema).args_schema.schema()"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-31T02:08:26.267596Z",
     "start_time": "2024-10-31T02:08:26.259034Z"
    }
   },
   "id": "251b8b4bb8b5946d",
   "execution_count": 49
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 支持字符串输入"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "f084a2247eaaec29"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "'baz'"
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def f(x: str) -> str:\n",
    "    return x + \"a\"\n",
    "\n",
    "\n",
    "def g(x: str) -> str:\n",
    "    return x + \"z\"\n",
    "\n",
    "\n",
    "runnable = RunnableLambda(f) | g\n",
    "as_tool = runnable.as_tool()\n",
    "as_tool.invoke(\"b\")\n",
    "\n"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-31T02:08:26.277233Z",
     "start_time": "2024-10-31T02:08:26.269110Z"
    }
   },
   "id": "4bdcbbf6273ac5c8",
   "execution_count": 50
  },
  {
   "cell_type": "markdown",
   "source": [
    "# 代理\n",
    "## RAG\n",
    "**API Reference:**[Document](https://python.langchain.com/api_reference/core/documents/langchain_core.documents.base.Document.html) | [InMemoryVectorStore](https://python.langchain.com/api_reference/core/vectorstores/langchain_core.vectorstores.in_memory.InMemoryVectorStore.html) | [OpenAIEmbeddings](https://python.langchain.com/api_reference/openai/embeddings/langchain_openai.embeddings.base.OpenAIEmbeddings.html)\n",
    "\n",
    "> 依赖`poetry add langgraph`\n"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "e65905c0f3cccc90"
  },
  {
   "cell_type": "code",
   "outputs": [],
   "source": [
    "from langchain_community.embeddings import DashScopeEmbeddings\n",
    "from langchain_core.vectorstores import InMemoryVectorStore\n",
    "from langchain_core.documents import Document\n",
    "import os\n",
    "\n",
    "from dotenv import load_dotenv\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "load_dotenv()\n",
    "\n",
    "llm = ChatOpenAI(\n",
    "    # 若没有配置环境变量，请用百炼API Key将下行替换为：api_key=\"sk-xxx\",\n",
    "    openai_api_key=os.getenv(\"DASHSCOPE_API_KEY\"),\n",
    "    openai_api_base=\"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n",
    "    model_name=\"qwen-max\",\n",
    "    temperature=0,\n",
    ")\n",
    "\n",
    "documents = [\n",
    "    Document(\n",
    "        page_content=\"Dogs are great companions, known for their loyalty and friendliness.\",\n",
    "    ),\n",
    "    Document(\n",
    "        page_content=\"Cats are independent pets that often enjoy their own space.\",\n",
    "    ),\n",
    "]\n",
    "\n",
    "vectorstore = InMemoryVectorStore.from_documents(\n",
    "    documents, embedding=DashScopeEmbeddings(dashscope_api_key=os.getenv(\"DASHSCOPE_API_KEY\"))\n",
    ")\n",
    "retriever = vectorstore.as_retriever(\n",
    "    search_type=\"similarity\",\n",
    "    search_kwargs={\"k\": 1},\n",
    ")"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-31T02:08:26.898711Z",
     "start_time": "2024-10-31T02:08:26.278785Z"
    }
   },
   "id": "62914eee350173d8",
   "execution_count": 51
  },
  {
   "cell_type": "markdown",
   "source": [
    "接下来，我们将创建一个简单的预构建LangGraph代理，并为其提供工具：\n",
    ">备注：存在版本依赖问题，暂时不能正常运行"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "f103bf5aece7d836"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "ename": "ImportError",
     "evalue": "cannot import name 'get_all_basemodel_annotations' from 'langchain_core.tools.base' (/opt/anaconda3/envs/ai_312/lib/python3.12/site-packages/langchain_core/tools/base.py)",
     "output_type": "error",
     "traceback": [
      "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
      "\u001B[0;31mImportError\u001B[0m                               Traceback (most recent call last)",
      "Cell \u001B[0;32mIn[52], line 1\u001B[0m\n\u001B[0;32m----> 1\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mlanggraph\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mprebuilt\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m create_react_agent\n\u001B[1;32m      3\u001B[0m tools \u001B[38;5;241m=\u001B[39m [\n\u001B[1;32m      4\u001B[0m     retriever\u001B[38;5;241m.\u001B[39mas_tool(\n\u001B[1;32m      5\u001B[0m         name\u001B[38;5;241m=\u001B[39m\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mpet_info_retriever\u001B[39m\u001B[38;5;124m\"\u001B[39m,\n\u001B[1;32m      6\u001B[0m         description\u001B[38;5;241m=\u001B[39m\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mGet information about pets.\u001B[39m\u001B[38;5;124m\"\u001B[39m,\n\u001B[1;32m      7\u001B[0m     )\n\u001B[1;32m      8\u001B[0m ]\n\u001B[1;32m      9\u001B[0m agent \u001B[38;5;241m=\u001B[39m create_react_agent(llm,tools)\n",
      "File \u001B[0;32m/opt/anaconda3/envs/ai_312/lib/python3.12/site-packages/langgraph/prebuilt/__init__.py:3\u001B[0m\n\u001B[1;32m      1\u001B[0m \u001B[38;5;124;03m\"\"\"langgraph.prebuilt exposes a higher-level API for creating and executing agents and tools.\"\"\"\u001B[39;00m\n\u001B[0;32m----> 3\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mlanggraph\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mprebuilt\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mchat_agent_executor\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m create_react_agent\n\u001B[1;32m      4\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mlanggraph\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mprebuilt\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mtool_executor\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m ToolExecutor, ToolInvocation\n\u001B[1;32m      5\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mlanggraph\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mprebuilt\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mtool_node\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m (\n\u001B[1;32m      6\u001B[0m     InjectedState,\n\u001B[1;32m      7\u001B[0m     InjectedStore,\n\u001B[1;32m      8\u001B[0m     ToolNode,\n\u001B[1;32m      9\u001B[0m     tools_condition,\n\u001B[1;32m     10\u001B[0m )\n",
      "File \u001B[0;32m/opt/anaconda3/envs/ai_312/lib/python3.12/site-packages/langgraph/prebuilt/chat_agent_executor.py:19\u001B[0m\n\u001B[1;32m     17\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mlanggraph\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mmanaged\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m IsLastStep, RemainingSteps\n\u001B[1;32m     18\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mlanggraph\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mprebuilt\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mtool_executor\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m ToolExecutor\n\u001B[0;32m---> 19\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mlanggraph\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mprebuilt\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mtool_node\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m ToolNode\n\u001B[1;32m     20\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mlanggraph\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mstore\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mbase\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m BaseStore\n\u001B[1;32m     21\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mlanggraph\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mtypes\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m Checkpointer\n",
      "File \u001B[0;32m/opt/anaconda3/envs/ai_312/lib/python3.12/site-packages/langgraph/prebuilt/tool_node.py:37\u001B[0m\n\u001B[1;32m     35\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mlangchain_core\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mtools\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m BaseTool, InjectedToolArg\n\u001B[1;32m     36\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mlangchain_core\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mtools\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m tool \u001B[38;5;28;01mas\u001B[39;00m create_tool\n\u001B[0;32m---> 37\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mlangchain_core\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mtools\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mbase\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m get_all_basemodel_annotations\n\u001B[1;32m     38\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mtyping_extensions\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m Annotated, get_args, get_origin\n\u001B[1;32m     40\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mlanggraph\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01merrors\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m GraphInterrupt\n",
      "\u001B[0;31mImportError\u001B[0m: cannot import name 'get_all_basemodel_annotations' from 'langchain_core.tools.base' (/opt/anaconda3/envs/ai_312/lib/python3.12/site-packages/langchain_core/tools/base.py)"
     ]
    }
   ],
   "source": [
    "\n",
    "from langgraph.prebuilt import create_react_agent\n",
    "\n",
    "tools = [\n",
    "    retriever.as_tool(\n",
    "        name=\"pet_info_retriever\",\n",
    "        description=\"Get information about pets.\",\n",
    "    )\n",
    "]\n",
    "agent = create_react_agent(llm,tools)\n",
    "\n",
    "for chunk in agent.stream({\"messages\": [(\"human\", \"What are dogs known for?\")]}):\n",
    "    print(chunk)\n",
    "    print(\"----\")"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-31T02:08:27.026106Z",
     "start_time": "2024-10-31T02:08:26.902011Z"
    }
   },
   "id": "43e2ffeb48d1f15d",
   "execution_count": 52
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
